﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;

namespace NTUTMessagerAPI
{
    public class RSA_API
    {
        static int RSA_BYTE_LIMIT = 117;
        static int RSA_BYTE_NUM = 128;


        public static byte[] RSAEncrypt(byte[] DataToEncrypt, String array, bool DoOAEPPadding)
        {
            RSAParameters encryptParameter = new RSAParameters();
            byte[] exponent = new byte[] { 0x01, 0x00, 0x01 };
            encryptParameter.Exponent = exponent;

            encryptParameter.Modulus = StringToByte(array);

            Byte[] export =  RSAEncrypt(DataToEncrypt, encryptParameter, DoOAEPPadding);

            if (DataToEncrypt.Length > 4)
            {
                Console.WriteLine("** RSA 加密　[" + RSA_API.ByteToString(DataToEncrypt) + "] => [" + RSA_API.ByteToString(export) + "]");
            }

            return export;
        }

        public static byte[] RSAEncrypt(byte[] DataToEncrypt, byte[] array, bool DoOAEPPadding)
        {
            RSAParameters encryptParameter = new RSAParameters();
            byte[] exponent = new byte[] { 0x01, 0x00, 0x01 };
            encryptParameter.Exponent = exponent;
            encryptParameter.Modulus = array;



            Byte[] export = RSAEncrypt(DataToEncrypt, encryptParameter, DoOAEPPadding);
            if (DataToEncrypt.Length > 4)
            {
                Console.WriteLine("** RSA 加密　[" + RSA_API.ByteToString(DataToEncrypt) + "] => [" + RSA_API.ByteToString(export) + "]");
            }

            return export;
        }

        public static byte[] RSAEncrypt(byte[] DataToEncrypt, RSAParameters RSAKeyInfo, bool DoOAEPPadding)
        {
            try
            {
                //Create a new instance of RSACryptoServiceProvider.
                RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();

                //Import the RSA Key information. This only needs
                //toinclude the public key information.
                RSA.ImportParameters(RSAKeyInfo);

                //Encrypt the passed byte array and specify OAEP padding.  
                //OAEP padding is only available on Microsoft Windows XP or
                //later.  

                int cut = (int)Math.Floor(DataToEncrypt.Length / (float)RSA_BYTE_LIMIT);
                byte[] encryptData = new byte[(cut + 1) * RSA_BYTE_NUM];

                //byte[] originEncrypt = RSA.Encrypt(DataToEncrypt, DoOAEPPadding);

                for (int i = 0; i < cut; i++)
                {
                    byte[] tempData = new byte[RSA_BYTE_LIMIT];
                    Array.Copy(DataToEncrypt, i * RSA_BYTE_LIMIT, tempData, 0, RSA_BYTE_LIMIT);

                    byte[] tempencryptData = RSA.Encrypt(tempData, DoOAEPPadding);
                    Array.Copy(tempencryptData, 0, encryptData, i * RSA_BYTE_NUM, RSA_BYTE_NUM);
                }

                int remainByteNum = DataToEncrypt.Length - RSA_BYTE_LIMIT * cut;

                byte[] tempRemainData = new byte[remainByteNum];
                Array.Copy(DataToEncrypt, RSA_BYTE_LIMIT * cut, tempRemainData, 0, remainByteNum);
                byte[] remainEncryptData = RSA.Encrypt(tempRemainData, DoOAEPPadding);
                Array.Copy(remainEncryptData, 0, encryptData, RSA_BYTE_NUM * cut, RSA_BYTE_NUM);


                return encryptData;
            }
            //Catch and display a CryptographicException  
            //to the console.
            catch (CryptographicException e)
            {
                Console.WriteLine(e.Message);

                return null;
            }

        }

        static public byte[] RSADecrypt(byte[] DataToDecrypt, RSAParameters RSAKeyInfo, bool DoOAEPPadding)
        {
            try
            {
                //Create a new instance of RSACryptoServiceProvider.
                RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();

                //Import the RSA Key information. This needs
                //to include the private key information.
                RSA.ImportParameters(RSAKeyInfo);

                int cut = (int)Math.Floor(DataToDecrypt.Length / (float)RSA_BYTE_NUM) + 1;

                byte[] decryptData = new byte[0];
                for (int i = 1; i < cut; i++)
                {
                    byte[] tempData = new byte[RSA_BYTE_NUM];
                    Array.Copy(DataToDecrypt, (i - 1) * RSA_BYTE_NUM, tempData, 0, RSA_BYTE_NUM);
                    byte[] tempDecryptData = RSA.Decrypt(tempData, DoOAEPPadding);
                    decryptData = ByteConnect(decryptData, tempDecryptData);
                }
                //Decrypt the passed byte array and specify OAEP padding.  
                //OAEP padding is only available on Microsoft Windows XP or
                //later.  

                if (decryptData.Length > 4)
                {
                    Console.WriteLine("** RSA 解密　[" + RSA_API.ByteToString(DataToDecrypt) + "] => [" + RSA_API.ByteToString(decryptData) + "]");
                }


                return decryptData;
            }
            //Catch and display a CryptographicException  
            //to the console.
            catch (CryptographicException e)
            {
                Console.WriteLine(e.ToString());

                return null;
            }

        }

        private static byte[] ByteConnect(byte[] first, byte[] second)
        {
            List<byte> connect = new List<byte>();
            connect.AddRange(first);
            connect.AddRange(second);
            return connect.ToArray();
        }

        public static int MeasureByteNumber(int originLength)
        {
            int realNum = (int)Math.Ceiling(originLength / 117.0f) * RSA_BYTE_NUM;
            if (originLength % 117 == 0)
            {
                realNum += RSA_BYTE_NUM;
            }

            return realNum;
        }


        static public String ByteToString(Byte[] array)
        {
            String all = "";

            foreach (Byte b in array)
            {
                all += b.ToString() + ",";
            }

            return all;
        }

        static public byte[] StringToByte(String str)
        {
            List<Byte> byteArray = new List<Byte>();
            String[] data = str.Split(",".ToCharArray());

            foreach (String s in data)
            {
                if (s.Trim().Length > 0)
                {
                    byteArray.Add(Convert.ToByte(s));
                }
            }

            return byteArray.ToArray();
        }

    }
}
